#include "qmaincanvas.h"

QMainCanvas::QMainCanvas(QMainCore *_core, QWidget *parent) : QLabel(parent), core(_core)
{
    p1 = p2 = QPointF(-1,-1);
    id = -1;

    showNodeNumber = false;
    showEdgeNumber = false;
    showAreaNumber = false;

    vectorFieldMode = false;
    colorFieldMode = false;
}

void QMainCanvas::paintEvent(QPaintEvent *)
{
    QPainter painter;
    painter.begin(this);
    painter.setRenderHint(QPainter::Antialiasing);

    this->drawModel(painter);
    this->drawInformation(painter);

    painter.setBrush(QBrush(QColor(0,0,255,31)));

    switch (mode)
    {
    case 00://Cell edit mode
        break;
    case 10://Node edit mode
        break;
    case 20://Edge edit mode
        if((p1.x()>=0)&&(p1.y()>=0))
            painter.drawLine(p1,p2);
        break;
    case 30://Area edit mode
        if ((p1.x()>=0)&&(p1.y()>=0))
        {
            painter.setBrush(QBrush(QColor(0,0,255,31)));
            painter.setPen(Qt::NoPen);
            painter.drawRect(QRectF(qMin(p1.x(), p2.x()), qMin(p1.y(),p2.y()), qAbs(p1.x()-p2.x()), qAbs(p1.y()-p2.y())));
        }
        break;
    }

    painter.end();
}

void QMainCanvas::drawModel(QPainter &painter)
{
    QBrush frontalNodeBrush(Qt::gray);
    QBrush nodeBrush(Qt::white);
    QBrush adhesionBrush(QColor(127, 0, 0, 63));

    QPen boundaryNodePen(Qt::darkMagenta);
    QPen nodePen(QBrush(Qt::black), 1);
    QPen nodeNumberPen(Qt::black);

    QPen frontEdgePen(Qt::darkMagenta);
    QPen edgePen(Qt::black);

    QColor areaColor(0,0,255,31);
    QPen areaNumberPen(Qt::blue);

    QPointF space = QPointF(0,0);

    double alpha;
    double f;

    painter.setPen(areaNumberPen);
    for (int i=0; i<cell.areas.length(); ++i)
    {
        painter.fillRect(cell.areas[i].rect, areaColor);
        painter.fillRect(QRectF((cell.areas[i].rect.topLeft()+cell.areas[i].rect.bottomRight())/2-QPointF(10,10),
                                (cell.areas[i].rect.topLeft()+cell.areas[i].rect.bottomRight())/2+QPointF(10,10)), Qt::Dense7Pattern);

        if (showAreaNumber) painter.drawText(cell.areas[i].rect.topLeft() + space, QString::number(i));
    }

    if (cell.frontNodesCount>0)
    {
        painter.setPen(QPen(QBrush(Qt::red), 2, Qt::DotLine));
        painter.drawPolyline(cell.fCPosPolygon);
    }

    if ((!vectorFieldMode)&&(!colorFieldMode))
    {
        for (int i=0; i<cell.edges.length(); ++i)
        {
            if (cell.edges[i].isBoundary) painter.setPen(frontEdgePen);
            else painter.setPen(edgePen);

            painter.drawLine(cell.nodes[cell.edges[i].id1].pos, cell.nodes[cell.edges[i].id2].pos);

            if (showEdgeNumber) painter.drawText((cell.nodes[cell.edges[i].id1].pos+cell.nodes[cell.edges[i].id2].pos)/2 + space, QString::number(i));
        }

        for (int i=0; i<cell.nodes.length(); ++i)
        {
            painter.setPen(QPen(adhesionBrush, 1));
            painter.setBrush(adhesionBrush);
            if (!cell.nodes[i].isFrontal)
            {
                painter.drawLine(cell.nodes[i].adhesionPos, cell.nodes[i].pos);
                painter.drawEllipse(cell.nodes[i].adhesionPos,3,3);
            }

            if (cell.nodes[i].isBoundary) painter.setPen(boundaryNodePen);
            else painter.setPen(nodePen);

            if (cell.nodes[i].isFrontal)
            {
                painter.setBrush(frontalNodeBrush);
                alpha = atan2(cell.nodes[i].fy, cell.nodes[i].fx);
                f = sqrt(pow(cell.nodes[i].fx,2) + pow(cell.nodes[i].fy,2));
                this->drawArrow(painter, cell.nodes[i].pos, QPointF(cell.nodes[i].pos.x() + 1E10*f*cos(alpha), cell.nodes[i].pos.y() + 1E10*f*sin(alpha)));
            }
            else painter.setBrush(nodeBrush);

            painter.drawEllipse(cell.nodes[i].pos,3,3);

            if (showNodeNumber)
            {
                painter.setPen(nodeNumberPen);
                painter.drawText(cell.nodes[i].pos + space, QString::number(i));
            }
        }
    }
    else if (colorFieldMode)
    {
        painter.setPen(Qt::black);

        QPolygonF polygon;
        double meanFxMonitor;
        double meanFyMonitor;

        for (int i=0; i<cell.edges.length(); ++i)
        {
            polygon.clear();

            meanFxMonitor = cell.nodes[cell.edges[i].id1].fxMonitor;
            meanFyMonitor = cell.nodes[cell.edges[i].id1].fyMonitor;
            meanFxMonitor += cell.nodes[cell.edges[i].id2].fxMonitor;
            meanFyMonitor += cell.nodes[cell.edges[i].id2].fyMonitor;
            meanFxMonitor += cell.nodes[cell.edges[i].id3].fxMonitor;
            meanFyMonitor += cell.nodes[cell.edges[i].id3].fyMonitor;
            meanFxMonitor /= 3;
            meanFyMonitor /= 3;

            painter.setBrush(this->getColorByAngle(atan2(meanFyMonitor, meanFxMonitor) - atan2((cell.fCPos - cell.cPos).y(), (cell.fCPos - cell.cPos).x())));

            polygon << cell.nodes[cell.edges[i].id1].pos << cell.nodes[cell.edges[i].id2].pos
                    << cell.nodes[cell.edges[i].id3].pos << cell.nodes[cell.edges[i].id1].pos;

            painter.drawPolygon(polygon);

            if (!cell.edges[i].isBoundary)
            {
                polygon.clear();

                meanFxMonitor = cell.nodes[cell.edges[i].id1].fxMonitor;
                meanFyMonitor = cell.nodes[cell.edges[i].id1].fyMonitor;
                meanFxMonitor += cell.nodes[cell.edges[i].id2].fxMonitor;
                meanFyMonitor += cell.nodes[cell.edges[i].id2].fyMonitor;
                meanFxMonitor += cell.nodes[cell.edges[i].id4].fxMonitor;
                meanFyMonitor += cell.nodes[cell.edges[i].id4].fyMonitor;
                meanFxMonitor /= 3;
                meanFyMonitor /= 3;

                painter.setBrush(this->getColorByAngle(atan2(meanFyMonitor, meanFxMonitor) - atan2((cell.fCPos - cell.cPos).y(), (cell.fCPos - cell.cPos).x())));

                polygon << cell.nodes[cell.edges[i].id1].pos << cell.nodes[cell.edges[i].id2].pos
                        << cell.nodes[cell.edges[i].id4].pos << cell.nodes[cell.edges[i].id1].pos;

                painter.drawPolygon(polygon);
            }
        }
    }
    else
    {
        for (int i=0; i<cell.nodes.length(); ++i)
        {
            alpha = atan2(cell.nodes[i].fyMonitor, cell.nodes[i].fxMonitor);
            //f = sqrt(pow(cell.nodes[i].fxMonitor, 2) + pow(cell.nodes[i].fyMonitor, 2));
            f = 8;
            painter.setPen(this->getColorByAngle(atan2(cell.nodes[i].fyMonitor, cell.nodes[i].fxMonitor) - atan2((cell.fCPos - cell.cPos).y(), (cell.fCPos - cell.cPos).x())));
            this->drawArrow(painter, QPointF(cell.nodes[i].pos.x() - f*cos(alpha), cell.nodes[i].pos.y() - f*sin(alpha)),
                            QPointF(cell.nodes[i].pos.x() + f*cos(alpha), cell.nodes[i].pos.y() + f*sin(alpha)));
            if (showNodeNumber) painter.drawText(cell.nodes[i].pos + space, QString::number(i));
        }
    }
}

void QMainCanvas::drawInformation(QPainter &painter)
{
    QPen oldPen = painter.pen();
    painter.setPen(Qt::black);

    QPointF pos = this->mapFromParent(QPoint(this->parentWidget()->width()-270, 30));
    QPointF xstep = QPointF(120, 0);
    QPointF ystep = QPointF(0, 15);
    QPointF margin = QPointF(10, 10);

    painter.fillRect(QRectF(pos - ystep - margin, pos + 2*xstep + ystep + margin), QBrush(QColor(255,255,255,127)));

    int hours = static_cast<int>(cell.t)/3600;
    int minutes = static_cast<int>(cell.t - 3600*(static_cast<int>(cell.t)/3600))/60;

    painter.drawText(pos + 0*ystep + 0*xstep, "time [h:m]: ");
    painter.drawText(pos + 0*ystep + 1*xstep, QString::number(hours) + ":" + QString::number(minutes));
    painter.drawText(pos + 1*ystep + 0*xstep, "velocity [mcm/min]: ");
    painter.drawText(pos + 1*ystep + 1*xstep, QString::number(cell.velocity));

    painter.setPen(oldPen);
}

void QMainCanvas::drawArrow(QPainter& painter, const QPointF& p1, const QPointF& p2, double angle)
{
    QPointF p = (p2-p1);
    double tangent = atan2(p.y(), p.x());
    double l = sqrt(pow(p.x(),2)+pow(p.y(),2))/3;

    QBrush oldBrush = painter.brush();
    painter.setBrush(QColor(painter.pen().color()));

    painter.drawLine(p1, p2);

    QPolygonF arrow;
    arrow << p2;
    arrow << QPointF(-l*cos(tangent+angle) + p2.x(), -l*sin(tangent+angle) + p2.y());
    arrow << p2-p/6;
    arrow << QPointF(-l*cos(tangent-angle) + p2.x(), -l*sin(tangent-angle) + p2.y());
    arrow << p2;

    painter.drawPolygon(arrow);

    painter.setBrush(oldBrush);
}

void QMainCanvas::mousePressEvent(QMouseEvent *event)
{
    if (!core->isRunning())
    {
        if (event->button() == Qt::LeftButton)
        {
            id = -1;
            switch (mode)
            {
            case 00://Cell edit mode
                id = core->getNearestNode(event->pos());
                break;
            case 10://Node edit mode
                id = core->getNearestNode(event->posF());
                if(id<0) core->addNode(event->posF());
                break;
            case 20://Edge edit mode
                p1 = p2 = event->posF();
                break;
            case 30://Area edit mode
                id = core->getNearestArea(event->posF());
                if(id<0) p1 = p2 = event->posF();
                break;
            }
        }
        else if (event->button() == Qt::RightButton)
        {
            id = -1;
            switch (mode)
            {
            case 00://Cell edit mode
                break;
            case 10://Node edit mode
                core->toggleNodeBoundary(event->posF());
                break;
            case 20://Edge edit mode
                break;
            case 30://Area edit mode
                break;
            }
        }
        else if (event->button() == Qt::MidButton)
        {
            id = -1;
            switch (mode)
            {
            case 00://Cell edit mode
                break;
            case 10://Node edit mode
                core->toggleNodeFrontal(event->posF());
                break;
            case 20://Edge edit mode
                break;
            case 30://Area edit mode
                break;
            }
        }
    }
}

void QMainCanvas::mouseMoveEvent(QMouseEvent *event)
{
    if(!core->isRunning())
    {
        switch (mode)
        {
        case 00://Cell edit mode
            if(id>=0) core->setModelPos(id,event->posF());
            break;
        case 10://Node edit mode
            if(id>=0) core->setNodePos(id,event->posF());
            break;
        case 20://Edge edit mode
            if((p1.x()>=0)&&(p1.y()>=0)) p2 = event->posF();
            break;
        case 30://Area edit mode
            if((p1.x()>=0)&&(p1.y()>=0)) p2 = event->posF();
            else if(id>=0) core->setAreaPos(id,event->posF());
            break;
        }

        this->update();
    }
}

void QMainCanvas::mouseDoubleClickEvent(QMouseEvent *event)
{
    if (event->button() == Qt::RightButton)
    {
        if (!core->isRunning())
        {
            QCellEditDialog *cellEditDialog = new QCellEditDialog(core, this);
            cellEditDialog->show();
        }
    }
}

void QMainCanvas::mouseReleaseEvent(QMouseEvent *event)
{
    if (!core->isRunning())
    {
        if (event->button() == Qt::LeftButton)
        {
            switch (mode)
            {
            case 00://Cell edit mode
                break;
            case 10://Node edit mode
                break;
            case 20://Edge edit mode
                core->addEdge(p1,p2);;
                break;
            case 30://Area edit mode
                core->addArea(QRectF(qMin(p1.x(), p2.x()), qMin(p1.y(),p2.y()), qAbs(p1.x()-p2.x()), qAbs(p1.y()-p2.y())));
                break;
            }
            p1 = p2 = QPointF(-1,-1);
        }
        this->update();
    }
}

void QMainCanvas::setCell(const CELL &_cell)
{
    cell = _cell;
    this->update();
}

void QMainCanvas::setMode(int _mode)
{
    mode = _mode;
}

void QMainCanvas::saveSvg(const QString& fileName)
{
    QSvgGenerator generator;
    generator.setFileName(fileName);
    generator.setSize(QSize(this->width(), this->height()));
    generator.setViewBox(QRect(0, 0, this->width(), this->height()));
    generator.setTitle(tr("SVG Cell Motility File"));
    generator.setDescription(tr("SVG Model Representation"));

    QPainter painter;
    painter.begin(&generator);
    this->drawModel(painter);
    painter.end();
}

void QMainCanvas::setShowNodeNumber(bool _showNodeNumber)
{
    showNodeNumber = _showNodeNumber;
    this->update();
}

void QMainCanvas::setShowEdgeNumber(bool _showEdgeNumber)
{
    showEdgeNumber = _showEdgeNumber;
    this->update();
}

void QMainCanvas::setShowAreaNumber(bool _showAreaNumber)
{
    showAreaNumber = _showAreaNumber;
    this->update();
}

void QMainCanvas::setVectorFieldMode(bool _vectorFieldMode)
{
    vectorFieldMode = _vectorFieldMode;
    this->update();
}

void QMainCanvas::setColorFieldMode(bool _colorFieldMode)
{
    colorFieldMode = _colorFieldMode;
    this->update();
}

QColor QMainCanvas::getColorByAngle(double alpha)
{
    double a = cos(alpha);

    if (a > 0.5)
    {
        return QColor(255, static_cast<int>(510*(1-a)), 0);
    }
    else if (a > 0)
    {
        return QColor(static_cast<int>(510*a), 255, 0);
    }
    else if (a > -0.5)
    {
        return QColor(0, 255, -static_cast<int>(510*a));
    }
    else
    {
        return QColor(0, static_cast<int>(510*(1+a)), 255);
    }
}
